package ptij.ADSL.de.ATUC;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.data.expr.*;
import ptolemy.data.type.*;
import ptolemy.actor.*;

import ptij.ADSL.common.MuxDataFrame;
import ptij.ADSL.common.FastFECDataFrame;

//////////////////////////////////////////////////////////////////////////
//// FECTx
/**

@author Ivan Jeukens
*/
public class FECTx extends TypedAtomicActor {    

    public FECTx(CompositeEntity container, String name) throws
            NameDuplicationException, IllegalActionException {
        super(container, name);
	
	input = new TypedIOPort(this, "input", true, false);
	input.setTypeEquals(BaseType.OBJECT);
	
	fast = new TypedIOPort(this, "fast", false, true);
	fast.setTypeEquals(BaseType.OBJECT);

        inter = new TypedIOPort(this, "inter", false, true);
	inter.setTypeEquals(BaseType.OBJECT);

	AS0Bytes = new Parameter(this, "AS0Bytes", new IntToken(8));
	LS0Bytes = new Parameter(this, "LS0Bytes", new IntToken(8));
	
	AS0Map = new Parameter(this, "AS0Map", new BooleanToken(false));
	LS0Map = new Parameter(this, "LS0Map", new BooleanToken(false));
	
	S = new Parameter(this, "S", new IntToken(1));

        parityFast = new Parameter(this, "parityFast", new IntToken(2));
	parityInter = new Parameter(this, "parityInter", new IntToken(2));
    }

    ///////////////////////////////////////////////////////////////////
    ////                     ports and parameters                  ////
    
    public TypedIOPort input;    
    public TypedIOPort fast;
    public TypedIOPort inter;

    public Parameter AS0Bytes;
    public Parameter LS0Bytes;    
    public Parameter AS0Map;
    public Parameter LS0Map;    
    public Parameter S;
    public Parameter parityFast, parityInter;

    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    public void attributeChanged(Attribute attribute)
            throws IllegalActionException {
        if(attribute == AS0Bytes) {
	    IntToken it = (IntToken) AS0Bytes.getToken();
	    _AS0Bytes = it.intValue();            
        }
        else
        if(attribute == LS0Bytes) {
            IntToken it = (IntToken) LS0Bytes.getToken();
            _LS0Bytes = it.intValue();            
        }
        else
        if(attribute == AS0Map) {
            BooleanToken bt = (BooleanToken) AS0Map.getToken();
	    _AS0Map = bt.booleanValue();
        }
        else
        if(attribute == LS0Map) {
            BooleanToken bt = (BooleanToken) LS0Map.getToken();
	    _LS0Map = bt.booleanValue();   
        }
        else
        if(attribute == S) {
 	    IntToken it = (IntToken) S.getToken();
	    _S = it.intValue();           
        }
        else
        if(attribute == parityFast) {
	    IntToken it = (IntToken) parityFast.getToken();
            _nnFast = _kkFast + it.intValue();
        }
        else
        if(attribute == parityInter) {	
	    IntToken it = (IntToken) parityInter.getToken();
            _nnInter = _kkInter + it.intValue();
        }
	else {
            super.attributeChanged(attribute);
        }        
    }

    public void initialize() throws IllegalActionException {
        super.initialize();
		
	_kkFast = _kkInter = 1;
	if(!_AS0Map) {
	    _kkFast += _AS0Bytes;
	}
	else {
	    _kkInter += _AS0Bytes;
	}
	
	if(!_LS0Map) {
	    _kkFast += _LS0Bytes;
	}
	else {
	    _kkInter += _LS0Bytes;
	}
	
	if(!_AS0Map /* || !_AS1Map || ... */) {
 	    _kkFast += 1;
	}	
	if(!_AS0Map || !_LS0Map) {
	    _kkFast += 1;
	}
	if(_AS0Map) {
 	    _kkInter += 1;
	}	
	if(_AS0Map || _LS0Map) {
	    _kkInter += 1;
	}
        _kkInter *= _S;	

        _checkParameters();
        
	if(_nnFast != _kkFast) _initRSFast();
	if(_nnInter != _kkInter) _initRSInter();
	
	_frameCounter = 0;
	_interdata = new int[_nnInter];
	_intercount = 0;        
    }
    
    public void fire() throws IllegalActionException {     
        if(_frameCounter == 0) {
            ObjectToken ot = (ObjectToken) input.get(0);
	    MuxDataFrame mf = (MuxDataFrame) ot.getValue();
	    int [] data = mf.getAsArray();
	    if(_nnFast != _kkFast) {        
	        int [] parity = _rsEncode(data, _nnFast, _kkFast, Gg_polyFast);	
	
	        FastFECDataFrame df = _newFastFECDataFrame(data, parity);
	        fast.send(0, new ObjectToken(df));
	    }
	    else {
	        ot = new ObjectToken(_newFastFECDataFrame(data, new int[0]));
	        fast.send(0, ot);
	    }
	    _frameCounter = 1;
	}
        else
	if(_frameCounter < _S + 1) {
 	    ObjectToken ot = (ObjectToken) input.get(0);
	    MuxDataFrame mf = (MuxDataFrame) ot.getValue();
	    int [] w = mf.getAsArray();
	    for(int j = 0;j < w.length;j++) {
	        _interdata[_intercount] = w[j];
	        _intercount++;
	    }
  	    _frameCounter++;
	}
	else {
            if(_nnInter != _kkInter) {
	        int [] parity = _rsEncode(_interdata, _nnInter, _kkInter, Gg_polyInter);
	        for(int i = 0;i < (_nnInter - _kkInter);i++) 
	           _interdata[_kkInter + i] = parity[i];
  	    }
	    inter.broadcast(new ObjectToken(_interdata));
	    _frameCounter = 0;
	    _intercount = 0;
	}
    }     
  
    ///////////////////////////////////////////////////////////////////
    ////                         private methods                   ////
    
    private int [] _rsEncode(int data[], int nn, int kk, int Gg_poly[]) {

	int no_p = nn - kk, i, j, feedback;
	int bb[] = new int[no_p];
	
	for(i = 0;i < no_p;i++) bb[i] = 0;
	for(i = 0; i < kk; i++) { 
	    feedback = data[i] ^ bb[0];
	    
	    for(j = 0; j < no_p - 1; j++)
	        bb[j] = bb[j + 1] ^ _gfMul(Gg_poly[no_p - 1 - j], feedback);
	    
	    bb[no_p - 1] = _gfMul(Gg_poly[0], feedback);
	}
	
	return bb;
    }
    
    private FastFECDataFrame _newFastFECDataFrame(int [] data, int [] parity) {
    
        FastFECDataFrame df = new FastFECDataFrame(_AS0Map ? 0 : _AS0Bytes,
	    0, 0, 0, _LS0Map ? 0 : _LS0Bytes, 0, 0, parity.length);
	    
	df.setFastByte(data[0]);
	
	int k = 1;
	if(!_AS0Map) {
            for(int i = 0;i < _AS0Bytes;i++) {
	        df.setAS0Byte(data[k], i);
		k++;
	    }
	}

	if(!_LS0Map) {
            for(int i = 0;i < _LS0Bytes;i++) {
	        df.setLS0Byte(data[k], i);
		k++;
	    }
	}
	
	if(df.hasAEX()) {
	    df.setAEXByte(data[k]);
	    k++;
	}	
	if(df.hasLEX()) {
	    df.setLEXByte(data[k]);
	    k++;
	}
		
	for(int i = 0;i < parity.length;i++) df.setParityByte(parity[i], i);
	
	return df;
    }

    private int _modnn(int x) {
    
        while(x >= gf_nn_max) {
	    x -= gf_nn_max;
	    x = (x >> gf_mm) + (x & gf_nn_max);
	}	
	return x;
    }
    
    private int _gfMul(int m1, int m2) {
    
        int m1_i, m2_i, prod_i, result;

	if((m1 == 0) || (m2 == 0)) return(0);

	m1_i = Index_of[m1];
	m2_i = Index_of[m2];
	prod_i = _modnn(m1_i + m2_i);
	result = Alpha_to[prod_i];

	return(result);
    }

    private void _initRSFast() throws IllegalActionException {
	int i, j;

	if(_nnFast > gf_nn_max) {
	    throw new IllegalActionException(this, "nn value > pow(2,gf_mm)-1");
	}

	if(_kkFast > _nnFast) {
            throw new IllegalActionException(this, "kk value > nn");
	}

	Gg_polyFast = new int[_nnFast - _kkFast + 1];
	Gg_indFast = new int[_nnFast - _kkFast + 1];

	Gg_polyFast[0] = Alpha_to[1];
	Gg_polyFast[1] = 1;

	for(i = 2; i <= _nnFast - _kkFast; i++) {
	    Gg_polyFast[i] = 1;
	    for(j = i - 1; j > 0; j--) {
	        Gg_polyFast[j] = Gg_polyFast[j - 1] ^ 
		    _gfMul(Gg_polyFast[j], Alpha_to[1 + i - 1]);
	    }
	    Gg_polyFast[0] = _gfMul(Gg_polyFast[0], Alpha_to[1 + i - 1]);
	}
	for(i = 0; i <= _nnFast - _kkFast; i++) 
	    Gg_indFast[i] = Index_of[Gg_polyFast[i]];
    }

    private void _initRSInter() throws IllegalActionException {
	int i, j;

	if(_nnInter > gf_nn_max) {
	    throw new IllegalActionException(this, "nn value > pow(2,gf_mm)-1");
	}

	if(_kkInter > _nnInter) {
            throw new IllegalActionException(this, "kk value > nn");
	}

	Gg_polyInter = new int[_nnInter - _kkInter + 1];
	Gg_indInter = new int[_nnInter - _kkInter + 1];

	Gg_polyInter[0] = Alpha_to[1];
	Gg_polyInter[1] = 1;

	for(i = 2; i <= _nnInter - _kkInter; i++) {
	    Gg_polyInter[i] = 1;
	    for(j = i - 1; j > 0; j--) {
	        Gg_polyInter[j] = Gg_polyInter[j - 1] ^ 
		    _gfMul(Gg_polyInter[j], Alpha_to[1 + i - 1]);
	    }
	    Gg_polyInter[0] = _gfMul(Gg_polyInter[0], Alpha_to[1 + i - 1]);
	}
	for(i = 0; i <= _nnInter - _kkInter; i++)
	    Gg_indInter[i] = Index_of[Gg_polyInter[i]];
    }
       
    private void _checkParameters() throws IllegalActionException {

        if(_AS0Bytes < 0) {
	    throw new IllegalActionException(this, "AS0 Bytes < 0");	
	}
	
	if(_LS0Bytes < 0) {
	    throw new IllegalActionException(this, "LS0 Bytes < 0");
	}
	
	if(_S != 1 && _S != 2 && _S != 4 && _S != 8 && _S != 16) {
	    throw new IllegalActionException(this, "\nInvalid value(" +
	        _S + ") for parameter S");
	}	

	if(_nnFast > 255) {
	    throw new IllegalActionException(this, "Maximum Fast codeword"+
	        " size is 255");
	}
	int par = _nnFast - _kkFast;
	if((par < 0) || (par > 16) || (par % 2 != 0)) {
	    throw new IllegalActionException(this, "Invalid number(" + par +
	        ") of fast parity bytes");
	}	       

	if(_nnInter > 255) {
	    throw new IllegalActionException(this, "Maximum Interleaved codeword"+
	        " size is 255");
	}
        par = _nnInter - _kkInter;
	if((par < 0) || (par > 16) || (par % 2 != 0)) {
	    throw new IllegalActionException(this, "Invalid number(" + par +
	        ") of interleaved parity bytes");
	}
        if(par % _S != 0) {
	    throw new IllegalActionException(this, "Interleaved parity " +
	        " bytes should be an integer multiple of S!");
	}    
    }       

    ///////////////////////////////////////////////////////////////////
    ////                         private variables                 ////
      
    private int Gg_polyFast[];
    private int Gg_indFast[];

    private int Gg_polyInter[];
    private int Gg_indInter[];

    private final int Pp[] = { 1, 0, 0, 0, 1, 1, 1, 0, 1};

    private final int Alpha_to[] = { 
               1, 2, 4, 8, 16, 32, 64, 128, 
               29, 58, 116, 232, 205, 135, 19, 
	       38, 76, 152, 45, 90, 180, 117, 
	       234, 201, 143, 3, 6, 12, 24, 48, 
	       96, 192, 157, 39, 78, 156, 37, 
	       74, 148, 53, 106, 212, 181, 
	       119, 238, 193, 159, 35, 70, 
	       140, 5, 10, 20, 40, 80, 160, 93, 
	       186, 105, 210, 185, 111, 222, 161, 
	       95, 190, 97, 194, 153, 47, 94, 188, 
	       101, 202, 137, 15, 30, 60, 120, 240, 
	       253, 231, 211, 187, 107, 214, 177, 
	       127, 254, 225, 223, 163, 91, 182, 113, 
	       226, 217, 175, 67, 134, 17, 34, 
	       68, 136, 13, 26, 52, 104, 208, 189, 
	       103, 206, 129, 31, 62, 124, 248, 237, 
	       199, 147, 59, 118, 236, 197, 151, 
	       51, 102, 204, 133, 23, 46, 92, 184, 
	       109, 218, 169, 79, 158, 33, 66, 
	       132, 21, 42, 84, 168, 77, 154, 41,
	       82, 164, 85, 170, 73, 146, 57, 114, 
	       228, 213, 183, 115, 230, 209, 191,
	       99, 198, 145, 63, 126, 252, 229, 215,
	       179, 123, 246, 241, 255, 227, 219, 171,
	       75, 150, 49, 98, 196, 149, 55, 110, 220,
	       165, 87, 174, 65, 130, 25, 50, 100, 200,
	       141, 7, 14, 28, 56, 112, 224, 221, 167,
	       83, 166, 81, 162, 89, 178, 121, 242, 249,
	       239, 195, 155, 43, 86, 172, 69, 138, 9, 18,
	       36, 72, 144, 61, 122, 244, 245, 247, 
	       243, 251, 235, 203, 139, 11, 22, 44, 
	       88, 176, 125, 250, 233, 207, 131, 27, 
	       54, 108, 216, 173, 71, 142, 0 };

    private final int Index_of[] = { 
               255, 0, 1, 25, 2, 50, 26, 198, 3, 223, 
               51, 238, 27, 104, 199, 75, 4, 100, 
	       224, 14, 52, 141, 239, 129, 28, 193, 
	       105, 248, 200, 8, 76, 113, 5, 138, 101, 
	       47, 225, 36, 15, 33, 53, 147, 142, 218, 
	       240, 18, 130, 69, 29, 181, 194, 125, 106, 
	       39, 249, 185, 201, 154, 9, 120, 77, 228, 
	       114, 166, 6, 191, 139, 98, 102, 221, 48, 
	       253, 226, 152, 37, 179, 16, 145, 34, 136, 
	       54, 208, 148, 206, 143, 150, 219, 189, 
	       241, 210, 19, 92, 131, 56, 70, 64, 30, 
	       66, 182, 163, 195, 72, 126, 110, 107, 
	       58, 40, 84, 250, 133, 186, 61, 202, 94, 
	       155, 159, 10, 21, 121, 43, 78, 212, 229, 
	       172, 115, 243, 167, 87, 7, 112, 192, 247, 
	       140, 128, 99, 13, 103, 74, 222, 237, 49, 
	       197, 254, 24, 227, 165, 153, 119, 38, 
	       184, 180, 124, 17, 68, 146, 217, 35, 32, 
	       137, 46, 55, 63, 209, 91, 149, 188, 
	       207, 205, 144, 135, 151, 178, 220, 252, 
	       190, 97, 242, 86, 211, 171, 20, 42, 93, 
	       158, 132, 60, 57, 83, 71, 109, 65, 162, 
	       31, 45, 67, 216, 183, 123, 164, 118, 196, 
	       23, 73, 236, 127, 12, 111, 246, 108, 161, 
	       59, 82, 41, 157, 85, 170, 251, 96, 134, 
	       177, 187, 204, 62, 90, 203, 89, 95, 
	       176, 156, 169, 160, 81, 11, 245, 22, 
	       235, 122, 117, 44, 215, 79, 174, 213, 
	       233, 230, 231, 173, 232, 116, 214, 244, 
	       234, 168, 80, 88, 175 };

    private final static int gf_mm = 8;
    private final static int gf_nn_max = 255;
    private final static int RS_NN_MAX = 256;

    private int _frameCounter;
    private int _interdata[];
    private int _intercount;
    
    private int _AS0Bytes;
    private int _LS0Bytes;
    private boolean _AS0Map;
    private boolean _LS0Map;
    private int _S;
    
    private int _nnFast, _kkFast;
    private int _nnInter, _kkInter;
}
